package com.kk.datastructure.stack.poland;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * title: 中缀转后缀
 * 
 * @author 阿K 
 * 2020年12月2日 下午11:21:55
 */
public class FolandNotation {

	public static void main(String[] args) {
		// 将一个中缀表达式转成后缀表达式
		// 1、1+((2+3)×4)-5 => 转成 1 2 3 + 4 × + 5 –
		// 2、因为直接对str 进行操作，不方便，因此 先将 "1+((2+3)×4)-5" =》 中缀的表达式对应的List
		String str = "1+((2+3)*4)-5";
		List<String> list = toInfixExpressionList(str);
		System.out.println("中缀表达式对应的List=" + list); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
		// 3、将得到的中缀表达式对应的List => 后缀表达式对应的List
		List<String> suffixExpreesionList = parseSuffixExpreesionList(list);
		System.out.println("后缀表达式对应的List=" + suffixExpreesionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
		System.out.printf("expression=%d", calculate(suffixExpreesionList));
	}

	// 对逆波兰表达式进行运算
	public static int calculate(List<String> ls) {
		// 创建一个栈，用于存储依次弹出计算
		Stack<String> stack = new Stack<String>();

		// 遍历ls，计算入栈
		for (String item : ls) {
			// 使用正则取数，匹配多位数字（1 或者 12 等等...）
			if (item.matches("\\d+")) {
				// 入栈
				stack.push(item);
			} else {
				// pop出两个数，并运算， 再入栈
				int num2 = Integer.parseInt(stack.pop());
				int num1 = Integer.parseInt(stack.pop());
				int res = 0;
				if ("+".equals(item))
					res = num1 + num2;
				else if ("-".equals(item))
					res = num1 - num2;
				else if ("*".equals(item))
					res = num1 * num2;
				else if ("/".equals(item))
					res = num1 / num2;
				else
					throw new RuntimeException("运算符有误");
				// 运算后入栈
				stack.push("" + res);
			}
		}
		// 最后留在stack中的数据是运算结果
		return Integer.parseInt(stack.pop());
	}

	// 2、中缀list 转 后缀List
	// ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]
	public static List<String> parseSuffixExpreesionList(List<String> ls) {
		// 原本定义两个栈,一个符号栈S1，一个存储中间计算和最终结果的栈S2
		// 说明，栈S2 因为需要全程是没有pop出的，最终还得逆序输出一次，就改为 list
		Stack<String> s1 = new Stack<String>();// 符号栈
		List<String> s2 = new ArrayList<String>();// 原定的s2

		// 遍历中缀 list
		for (String item : ls) {
			// 若是一个数字(包括多位数和1-9)，加入s2
			if (item.matches("\\d+")) {
				s2.add(item);
			} else if ("(".equals(item)) {
				s1.push(item);
			} else if (")".equals(item)) {
				// 如果是右括号“)”，则依次弹出s1栈顶的运算符，并压入s2，
				// 直到遇到左括号为止，此时将这一对括号及其内的所有数字和运算符丢弃
				while (!s1.peek().equals("(")) {
					s2.add(s1.pop());
				}
				s1.pop();// 将 ( 弹出 s1栈， 消除小括号
			} else {
				// 当item的优先级小于等于s1栈顶运算符, 将s1栈顶的运算符弹出并加入到s2中，
				// 再次转到(4.1)与s1中新的栈顶运算符相比较
				while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
					s2.add(s1.pop());
				}
				// 将item压栈
				s1.push(item);
			}
		}

		// 将s1中剩余的最后一个运算符弹出并加入s2
		if (s1.size() != 0) {
			s2.add(s1.pop());
		}
		// 因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List
		return s2;
	}

	// 1、将中缀表达式字符串转成List
	public static List<String> toInfixExpressionList(String s) {
		// 定义一个List,存放中缀表达式
		List<String> ls = new ArrayList<String>();
		// 定义一个指针，用于遍历
		int index = 0;
		// 拼接多位数
		String str;
		// 存储每次遍历的字符
		char c;
		do {
			// 若 c 不是数字，则加入 ls集合
			if ((c = s.charAt(index)) > '9' || (c = s.charAt(index)) < '0') {
				ls.add("" + c);
				index++;// 指针后移
			} else {// 如果是一个数字，还得考虑多位数与否
					// 先将str 置成"",每次拼接前先清空上次的记录
				str = "";
				// 如果是数字，并且表达式内还有值
				while (index < s.length() && (c = s.charAt(index)) >= '0' && (c = s.charAt(index)) <= '9') {
					str += c;// 拼接多位数
					index++;
				}
				ls.add(str);
			}
		} while (index < s.length());
		return ls;
	}
}

// 编写一个类 Operation 可以返回一个运算符 对应的优先级
class Operation {
	private static int ADD = 1;
	private static int SUB = 1;
	private static int MUL = 2;
	private static int DIV = 2;

	// 写一个方法，返回对应的优先级数字
	public static int getValue(String operation) {
		int result = 0;
		switch (operation) {
		case "+":
			result = ADD;
			break;
		case "-":
			result = SUB;
			break;
		case "*":
			result = MUL;
			break;
		case "/":
			result = DIV;
			break;
		default:
			System.out.println("不存在该运算符" + operation);
			break;
		}
		return result;
	}

}